@CLASS
Json

@OPTIONS
locals


@auto[]
$self.iShift(0)
$self.bIndent(true)

$self.tRep[^table::create{from	to
\	\\
"	\"
^taint[^#08]	\b
^taint[^#0C]	\f
^taint[^#0A]	\n
^taint[^#0D]	\r
^taint[^#09]	\t
# это для javascript
#^#FF	\^#FF
#'	\'
}]






###############################################################################
@decode1[sText]
$self.sJson[$sText]
$result[^decode[]]
###########



###############################################################################
@decode[sJson][ch;hR]

$self.sJson[^self.sJson.trim[]]
$ch[^self.sJson.left(1)]

$result[]

^switch[$ch]{ 
	^case[^{]{
		$result[^decodeObject[]]
	}
	^case[^[]{
		$result[^decodeArray[]]
	}
	^case[";']{
		$result[^decodeString[$ch]]
	}
	^case[-]{
		$result[^decodeNumber[]]
	}
	^case[]{
		^dstop[внезапно наступил конец всего]
	}
	^case[DEFAULT]{
		^rem{ отрицательные числа? проверяй регуляркой }
		^if(^ch.int(-1) >= 0){
			$result[^decodeNumber[]]
		}{
			$result[^decodeWord[]]
		}
	}
}
###############################################################################



###############################################################################
# ключ : ЧТО-ТО, ключ : ЧТО-ТО
@decodeObject[][sJson;key;iIndex;hR]

$self.sJson[, ^self.sJson.trim[start;^{]]
$self.sJson[^self.sJson.trim[]]

$hR[^hash::create[]]

# найти ключи и декодировать объекты по очереди
$iIndex(0)
^while(^self.sJson.length[] > 0){
	$key[^self.sJson.match[^^\s*,\s*
		"?(\w+)"?
		\s*\:(.*)][gx]]
	
	^if(def $key.2){
		$self.sJson[$key.2]
#		$hR.[${iIndex}_$key.1][^decode[]]
		$hR.[$key.1][^decode[]]
	}{
		$self.sJson[^self.sJson.trim[]]
		$self.sJson[^self.sJson.trim[both;^}]]
		^break[]
	}
	
	^iIndex.inc[]
}

$result[$hR]
###############################################################################



###############################################################################
# ключ : ЧТО-ТО, ключ : ЧТО-ТО
@decodeArray[][sJson;tRx;iIndex;hR]

$hR[^hash::create[]]

$self.sJson[^self.sJson.trim[]]
$self.sJson[^self.sJson.trim[start;^[]]
$self.sJson[^self.sJson.trim[]]

# FIXME: тут некоторая натяжка с пустыми массивами...
^if(^self.sJson.left(1) eq ]){
	$self.sJson[^self.sJson.trim[start;^]]]
}{
	$self.sJson[, $self.sJson]
	$iIndex(0)
	^while(^self.sJson.length[] > 1){
		$tRx[^self.sJson.match[^^\s*,\s*(.*)][g]]
		
		^if(def $tRx.1){
			$self.sJson[$tRx.1]
			$hR.[$iIndex][^decode[]]
		}{
			$self.sJson[^self.sJson.trim[]]
			$self.sJson[^self.sJson.trim[both;^]]]
			^break[]
		}
		^iIndex.inc[]
	}
}

$result[$hR]
###############################################################################



###############################################################################
# строки в кавычках
@decodeString[sQuote][sJson;sString;tMtc;hR]
$self.sJson[^self.sJson.trim[start;$sQuote]]

# найти неэкранированную закрывающую кавычку
$tMtc[^self.sJson.match[(.*?[^^\\])\$sQuote][]]
$sString[$tMtc.1]

# если нет закрывашки - ошибка синтаксиса

$sRemainder[^self.sJson.right(^self.sJson.length[] - ^sString.length[] - 1)]

$self.sJson[$sRemainder]
$result[$sString]
###############################################################################



###############################################################################
# строки в кавычках
@decodeNumber[][sJson;sString;tMtc;hR]

$tMtc[^self.sJson.match[(\d+)][]]
$sString[$tMtc.1]

# если ничего не нашлось - ошибка синтаксиса

$sRemainder[^self.sJson.right(^self.sJson.length[] - ^sString.length[])]

$self.sJson[$sRemainder]
$result[$sString]
###############################################################################



###############################################################################
# имя переменной
@decodeWord[][sJson;sString;tMtc;hR]

$tMtc[^self.sJson.match[(\w+)][]]
$sString[$tMtc.1]

# если ничего не нашлось - ошибка синтаксиса

$sRemainder[^self.sJson.right(^self.sJson.length[] - ^sString.length[])]

$self.sJson[$sRemainder]
$result[$sString]
###############################################################################





@convert[o][result]
^if(def $o){
	^self.[show_$o.CLASS_NAME][$o]
}{
	^self.show_void[]
}


@GET_DEFAULT[o]
$result[$self.show_userclass]


@show_void[]
$result[null]


@show_userclass[o]
$result[$o.CLASS_NAME]


@show_bool[o]
$result[^if($o){true}{false}]


@show_string[o]
#$result[$o]
$result["^o.replace[$self.tRep]"]


@show_int[o]
$result[$o]


@show_double[o]
$result[$o]


@show_date[d]
$result[new Date(^d.unix-timestamp[]000)]


@show_hash[h][result]
	^if($self.bIndent){
		^self.iShift.inc[]
#		^self.foreach[$h;k;v]
		$sTabs[^for[i](2;$self.iShift){^#09}]
		$result[^{^#0A^h.foreach[k;v]{^#09$sTabs^self.show_key[$k]: ^self.convert[$v]}[,^#0A]^#0A$sTabs^}]
		^self.iShift.dec[]
	}{
		$result[^{^h.foreach[k;v]{^self.show_key[$k]:^self.convert[$v]}[,]}]
	}


@show_table[t][result]
	$tCol[^t.columns[]]
	$bNamless(false)
	^if(^t.count[] > 0 && ^tCol.count[] == 0){
		$bNamless(true)
		$tFlipped[^t.flip[]]
		$tCol[^table::create{column}]
		^for[i](0;$tFlipped-1){^tCol.append{$i}}
#		помогает для named таблиц, у которых колонки не заданы
		$t[^tFlipped.flip[]]
	}
	^if($self.bIndent){
		$sTabs[^for[i](1;$self.iShift){^#09}]
		$result[^[^#0A	$sTabs^t.menu{^{^#0A^tCol.menu{$sTabs		^self.show_key[$tCol.column]: ^convert[^if($bNamless){$t.[$tCol.column]}{$t.fields.[$tCol.column]}]}[,^#0A]^#0A	$sTabs^}}[,]^#0A$sTabs^]]
	}{
		$result[^[^t.menu{^{^tCol.menu{^self.show_key[$tCol.column]:^convert[^if($bNamless){$t.[$tCol.column]}{$t.fields.[$tCol.column]}]}[,]^}}[,]^]]
	}
	

# ключи ассоциативных массивов
@show_key[s][resultd]
#$result[^s.js-escape[]]
#$result[^s.match[(.*[^^a-zа-я0-9_\-].*)][i]{^self.show_string[$match.1]}]]
$result[^self.show_string[$s]]


@show_file[f]
$result[$f.name]


@show_image[i]
$result[$i.CLASS_NAME]


@show_xdoc[x]
$result[$x.CLASS_NAME]


@show_xnode[x]
$result[$x.CLASS_NAME]


# look over all hash elements with specified order
@foreach[hHash;sKeyName;sValueName;jCode;sSeparator;sDirection][tKey;result]
	$tKey[^hHash._keys[]]
	^try{
		^tKey.sort($tKey.key)[$sDirection]
	}{
		$exception.handled(true)
		^tKey.sort{$tKey.key}[$sDirection]
	}
	^tKey.menu{
		^if(def $sKeyName){
			$caller.[$sKeyName][$tKey.key]}
			^if(def $sValueName){
				^if($hHash.[$tKey.key] is double){
					$caller.[$sValueName]($hHash.[$tKey.key])
				}{
					$caller.[$sValueName][$hHash.[$tKey.key]]
				}
			}
		$jCode
	}[$sSeparator]

